#include <Ice/Application.h>
#include <IceUtil/Time.h>
#include <FileTransfer.h>

using namespace std;
using namespace Ice;
using namespace FileTransfer;

class File_nextI
  : public AMI_File_next,
    public IceUtil::Monitor<IceUtil::Mutex>
{
public:

  File_nextI() :
    _done(false)
  {
  }
  virtual void ice_response(const ::std::pair<const ::Ice::Byte*, const ::Ice::Byte*>&);
  virtual void ice_exception(const ::Ice::Exception&);
  void getData(::Ice::ByteSeq&);
 
private:

  bool _done;
  auto_ptr<Ice::Exception> _exception;
  Ice::ByteSeq _bytes;  
};

typedef IceUtil::Handle<File_nextI> File_nextIPtr;

void
File_nextI :: ice_response(const ::std::pair<const ::Ice::Byte*, const ::Ice::Byte*>& bytes)
{
  Lock sync(*this);
  Ice::ByteSeq(bytes.first, bytes.second).swap(_bytes);
  _done = true;
  notify();
}

void
File_nextI :: ice_exception(const ::Ice::Exception& ex)
{
  Lock sync(*this);
  _exception.reset(ex.ice_clone());
  _done = true;
  notify();
}

void
File_nextI :: getData(::Ice::ByteSeq& bytes)
{
  Lock sync(*this);
  while (!_done) {
    wait();
  }
  _done = false;
  if (_exception.get()) {
    auto_ptr<Ice::Exception> ex = _exception;
    _bytes.clear();
    ex->ice_throw();
  }
  bytes.swap(_bytes);
}

class Client
  : virtual public Ice::Application
{
public:
  virtual int run (int argc,
		   char *argv[])
  {
    Ice::ObjectPrx base = communicator()->
      stringToProxy("FileStore:tcp -h 161.67.38.113 -p 10000");
    FileStorePrx fileStorePrx = FileStorePrx::checkedCast(base);

    if (argc < 2) {
      cout << "Synopsis: ./Client <file_to_transfer>" << endl;
      return EXIT_FAILURE;
    }

    string name = argv[1];
    FILE* fp = fopen(name.c_str(), "wb");
    int len = 1000 * 1024;
    int rc = EXIT_SUCCESS;

    try {
      int num = 1000 * 1024;
      FilePrx file = fileStorePrx->read(name, num);

      int offset = 0;
      IceUtil::Time start = IceUtil::Time::now();
      File_nextIPtr curr, next;
      Ice::ByteSeq bytes;

      for (;;) {
	if (!curr) {
	  curr = new File_nextI;
	  next = new File_nextI;
	  file->next_async(curr);
	}
	else {
	  swap(curr, next);
	}
	file->next_async(next);
	curr->getData(bytes);
	if (bytes.empty()) {
	  break;
	}
	if (fwrite(&bytes[0], 1, bytes.size(), fp)
	    != bytes.size()) {
	  cerr << "error writing: " << strerror(errno) << endl;
	  rc = EXIT_FAILURE;
	}
	offset += bytes.size(); 
      }

      IceUtil::Time tm = IceUtil::Time::now() - start;
      cout << "Time employed: " << tm.toSecondsDouble() << endl;
    }
    catch (const FileAccessException& e) {
      cerr << "FileAccessException: " << e.reason << endl;
      rc = EXIT_FAILURE;
    }

    fclose(fp);

    communicator()->waitForShutdown();
    return rc;
  }
};

int main(int argc,
	 char* argv[])
{
  Client client;
  return client.main(argc, argv);
}
